package com.skycan.plc.adapter.service;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.skycan.plc.adapter.config.MqttConfig;
import com.skycan.plc.adapter.model.ControlCommand;
import com.skycan.plc.adapter.model.ControlCommand.CommandStatus;
import com.skycan.plc.adapter.model.DeviceInfo;
import com.skycan.plc.adapter.mqtt.MqttClient;
import com.skycan.plc.adapter.protocol.ProtocolAdapter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * 设备控制服务
 */
public class ControlService {
    private static final Logger logger = LoggerFactory.getLogger(ControlService.class);

    private final DeviceService deviceService;
    private final MqttClient mqttClient;
    private final ObjectMapper objectMapper;
    private final ExecutorService executorService;

    // 命令缓存，用于记录已发送的命令
    private final Map<String, ControlCommand> commandCache = new ConcurrentHashMap<>();

    // 命令接收主题
    private final String commandTopic;

    public ControlService(DeviceService deviceService) {
        this.deviceService = deviceService;
        this.mqttClient = MqttClient.getInstance();
        this.objectMapper = new ObjectMapper();
        this.executorService = Executors.newFixedThreadPool(5);
        this.commandTopic = MqttConfig.getInstance().getCommandTopic();
    }

    /**
     * 启动控制服务
     */
    public void start() {
        logger.info("设备控制服务启动");

        // 订阅命令主题
        mqttClient.subscribe(commandTopic, this::handleCommandMessage);
    }

    /**
     * 停止控制服务
     */
    public void stop() {
        logger.info("设备控制服务停止");

        // 取消订阅命令主题
        mqttClient.unsubscribe(commandTopic);

        // 关闭线程池
        if (executorService != null && !executorService.isShutdown()) {
            executorService.shutdown();
        }
    }

    /**
     * 发送控制命令
     *
     * @param command 控制命令
     * @return 命令ID
     */
    public String sendCommand(ControlCommand command) {
        // 如果没有命令ID，生成一个新的
        if (command.getCommandId() == null || command.getCommandId().isEmpty()) {
            command.setCommandId(UUID.randomUUID().toString());
        }

        // 设置命令时间戳
        if (command.getTimestamp() == 0) {
            command.setTimestamp(System.currentTimeMillis());
        }

        // 设置命令状态为等待执行
        command.setStatus(CommandStatus.PENDING);

        // 缓存命令
        commandCache.put(command.getCommandId(), command);

        // 异步执行命令
        executorService.submit(() -> executeCommand(command));

        return command.getCommandId();
    }

    /**
     * 处理MQTT接收到的命令消息
     *
     * @param message MQTT消息内容
     */
    private void handleCommandMessage(String message) {
        try {
            // 解析命令
            ControlCommand command = objectMapper.readValue(message, ControlCommand.class);

            logger.info("收到控制命令: {}, 设备={}, 地址={}, 值={}",
                    command.getCommandId(), command.getDeviceId(), command.getAddress(), command.getValue());

            // 发送控制命令
            sendCommand(command);
        } catch (Exception e) {
            logger.error("解析控制命令失败: {}", e.getMessage());
        }
    }

    /**
     * 执行控制命令
     *
     * @param command 控制命令
     */
    private void executeCommand(ControlCommand command) {
        String deviceId = command.getDeviceId();

        // 获取设备信息
        DeviceInfo deviceInfo = deviceService.getDeviceInfo(deviceId);
        if (deviceInfo == null) {
            logger.error("设备未注册: {}", deviceId);
            updateCommandStatus(command, CommandStatus.FAILED);
            return;
        }

        // 检查设备状态
        if (deviceInfo.getStatus() != DeviceInfo.DeviceStatus.ONLINE) {
            logger.error("设备不在线: {}, 当前状态: {}", deviceId, deviceInfo.getStatus());
            updateCommandStatus(command, CommandStatus.FAILED);
            return;
        }

        // 获取设备协议适配器
        ProtocolAdapter adapter = deviceService.getProtocolAdapter(deviceInfo.getDeviceType());
        if (adapter == null) {
            logger.error("未找到设备协议适配器: {}, 类型: {}", deviceId, deviceInfo.getDeviceType());
            updateCommandStatus(command, CommandStatus.FAILED);
            return;
        }

        try {
            // 更新命令状态为已发送
            updateCommandStatus(command, CommandStatus.SENT);

            // 写入数据点
            boolean success = adapter.writeDataPoint(deviceInfo, command);

            if (success) {
                logger.info("控制命令执行成功: {}, 设备={}, 地址={}, 值={}",
                        command.getCommandId(), deviceId, command.getAddress(), command.getValue());
                updateCommandStatus(command, CommandStatus.COMPLETED);
            } else {
                logger.error("控制命令执行失败: {}, 设备={}, 地址={}, 值={}",
                        command.getCommandId(), deviceId, command.getAddress(), command.getValue());
                updateCommandStatus(command, CommandStatus.FAILED);
            }
        } catch (Exception e) {
            logger.error("控制命令执行异常: {}, 设备={}, 错误: {}",
                    command.getCommandId(), deviceId, e.getMessage());
            updateCommandStatus(command, CommandStatus.FAILED);
        }
    }

    /**
     * 更新命令状态
     *
     * @param command 控制命令
     * @param status  命令状态
     */
    private void updateCommandStatus(ControlCommand command, CommandStatus status) {
        command.setStatus(status);
        commandCache.put(command.getCommandId(), command);

        // 发布命令状态更新
        try {
            String json = objectMapper.writeValueAsString(command);
            mqttClient.publish(commandTopic + "/status", json);
        } catch (Exception e) {
            logger.error("发布命令状态更新失败: {}", e.getMessage());
        }
    }

    /**
     * 获取命令状态
     *
     * @param commandId 命令ID
     * @return 命令状态
     */
    public CommandStatus getCommandStatus(String commandId) {
        ControlCommand command = commandCache.get(commandId);
        return command != null ? command.getStatus() : null;
    }

    /**
     * 获取命令信息
     *
     * @param commandId 命令ID
     * @return 命令信息
     */
    public ControlCommand getCommand(String commandId) {
        return commandCache.get(commandId);
    }
}